<template>
  <div class="menu-container">
    <!-- <Tree :data="data" show-checkbox></Tree>-->

    <!--    <div class="box">
          <Input v-model="select" placeholder="搜索" style="width: 200px"/>
        </div>-->

    <div class="tree">

      <div class="top-box">
        <div class="order" @click="orderBase()">
          <IconAli :sty="{width: '25px', height: '25px', margin: '2px 2px 0px 0px'}" icon='order-1'></IconAli>
        </div>

        <div class="add" @click="insertMenu(-1)">
          <IconAli :sty="{width: '20px', height: '22px', margin: '3px 54px 0px 0px'}" icon='add'></IconAli>
        </div>

      </div>

      <!--
      data:展示数据
      empty-text:内容为空展示的文本
      node-key:该节点的唯一标识
      props:{   //配置选项表
        label:    指定节点标签为节点对象的某个属性值
        children: 指定子树
        disabled: 指定节点选择框是否禁用,true为禁用
        isLeaf:   指定节点是否为叶子节点,只有在 lazy 属性生效
      }
      render-after-expand: 默认 true,加载时只加载第一层节点,此时子节点还没渲染到页面上,当点开某一个节点之后,会将子节点的 div 渲染出来,如果为 false,组件挂载时就将所有子节点渲染出来
      load: 加载子树的方法,只有当 lazy 为true生效,只有在懒加载的时候,才会用到该 load 方法
      lazy: 懒加载,默认 true
      render-content:树节点的渲染函数,Function(h, { node, data, store }
      highlight-current: 是否高亮当前选中节点,默认 不高亮 false
      default-expand-all: 是否默认展开所有节点,默认 不展开 false
      expand-on-click-node: 是否在点击节点时收缩,默认 true,点击节点收缩,  false只有在点击箭头才收缩
      check-on-click-node: 是否在点击节点选中节点,默认 false,不选中,  true只有当点击复选框才选中
      auto-expand-parent: 展开子节点时,是否自动展开父节点,默认 true
      default-expanded-keys:默认展开节点的 key 数组
      default-checked-keys: 默认勾选节点的 key 数组
      current-node-key:当前选中的节点
      show-checkbox:节点是否可以被选中,是否有checkbox,默认 false
      accordion:手风琴,只打开一个,默认 false
      filter-node-method: 对树节点进行筛选时执行的方法，返回 true 表示这个节点可以显示，返回 false 则表示这个节点会被隐藏 Function(value, data, node)
      indent: 父子节点缩进距离 number类型 单位像素
      icon-class: 自定义节点图标
      draggable:是否可以拖拽

      allow-drag: 判断能否拖拽 Function(node)
      allow-drop: Function(draggingNode, dropNode, type)  拖拽时判定目标节点能否被放置。type 参数有三种情况：'prev'、'inner' 和 'next'，分别表示放置在目标节点前、插入至目标节点和放置在目标节点后
      -->

      <!--
        方法：可以使用 this.$refs.tree.filter(val);
        filter(要匹配的内容): 对树节点筛选,接收的参数作为  filter-node-method 方法的第一个参数
        updateKeyChildren(节点Key,节点数据数组): 更新节点数据,必须绑定 node-key

        getCheckedNodes(是否是叶子节点false,是否包含半选节点false)：获取当前checkbox 状态下被选中的节点

        setCheckedNodes(节点数组)：勾选节点，传的是是数据

        getCheckedKeys
        setCheckedKeys(节点id数组)

        setChecked(节点Key 或数据，boole是否被选中，是否设置子节点false)：设置勾选属性，必须有 node-key

        getHalfCheckedNodes
        getHalfCheckedKeys   若节点可被选择（即 show-checkbox 为 true），则返回目前半选中的节点的 key 所组成的数组

        getCurrentNode
        getCurrentKey         获取当前被选中节点的 key，使用此方法必须设置 node-key 属性，若没有节点被选中则返回 null

        setCurrentKey(待被选节点的 key)       通过 key 设置某个节点的当前选中状态，使用此方法必须设置 node-key 属性

        setCurrentNode(node)  通过 node 设置某个节点的当前选中状态，使用此方法必须设置 node-key 属性

        getNode(data)       根据 data 或者 key 拿到 Tree 组件中的 node
        remove ((data) 要删除的节点的 data 或者 node)     	删除 Tree 中的一个节点，使用此方法必须设置 node-key 属性

        append(data, parentNode) 接收两个参数，1. 要追加的子节点的 data 2. 子节点的 parent 的 data、key 或者 node
        insertBefore	(data, refNode) 接收两个参数，1. 要增加的节点的 data 2. 要增加的节点的后一个节点的 data、key 或者 node
        insertAfter (data, refNode) 接收两个参数，1. 要增加的节点的 data 2. 要增加的节点的前一个节点的 data、key 或者 node
      -->
      <el-tree
          ref="tree"
          :data="data"
          :style="{width:'500px',display:'flex',flexDirection:'column',alignContent:'space-between'}"
          empty-text="此节点为空,请联系管理员"
          node-key="id"
          :render-after-expand="false"
          :render-content="renderContent"
          :highlight-current="true"
          :default-expand-all="true"
          :expand-on-click-node="false"
          :auto-expand-parent="true"
          :default-expanded-keys="openKeyArr"
          :default-checked-keys="checkKeyArr"
          :current-node-key="currentNodeKey"
          :show-checkbox="false"
          :filter-node-method="filterNode"
          :accordion="false"
          :indent=22
          icon-class=""
          draggable
          :allow-drag="allowDrag"
          :allow-drop="allowDrop"
          @node-click="nodeClick"
          @node-contextmenu="nodeContextMenu"
          @check-change="checkChange"
          @check="check"
          @current-change="currentChange"
          @node-expand="nodeExpand"
          @node-collapse="nodeCollapse"
          @node-drag-start="handleDragStart"
          @node-drag-enter="handleDragEnter"
          @node-drag-leave="handleDragLeave"
          @node-drag-over="handleDragOver"
          @node-drag-end="handleDragEnd"
          @node-drop="handleDrop">

      </el-tree>

    </div>

    <!--修改菜单-->
    <Modal
        v-model="updateFlag"
        title="修改菜单信息"
        :mask-closable="false"
        draggable
        sticky
        @on-ok="updateOk"
        @on-cancel="updateCancel">

      <el-form label-position="right" label-width="80px" :model="currentCheckNode">
        <el-form-item label="菜单名称">
          <el-input clearable v-model="currentCheckNode.menuName"></el-input>
        </el-form-item>
        <el-form-item label="菜单代号">
          <el-input clearable v-model="currentCheckNode.menuCode"></el-input>
        </el-form-item>
        <el-form-item label="菜单图标">
          <el-input clearable v-model="currentCheckNode.menuIcon"></el-input>
        </el-form-item>
      </el-form>
    </Modal>

    <Modal
        v-model="deleteFlag"
        title="删除菜单信息"
        :mask-closable="false"
        draggable
        sticky
        @on-ok="deleteOk">
      <template #header>
        <IconAli :sty="{width:'40px',height:'40px'}" icon='danger' style="margin:6px 0px 0px 16px"></IconAli>
      </template>

      <div style="display:flex;flex-direction:column;margin-left: 16px">
        <div class="delTip">是否删除以下菜单,此操作不可撤销,请谨慎操作!</div>
        <div class="nodeTip">{{ currentCheckNode.menuName }}</div>
      </div>

    </Modal>


    <!--排序-->
    <Modal
        v-model="orderFlag"
        title="请排序"
        :mask-closable="false"
        draggable
        sticky
        width="800px"
        @on-ok="orderMenuOk">

      <Order :dataList="orderMenuList" @my-event-order-list="getOrderMenuList"></Order>

    </Modal>
  </div>
</template>

<script>
import Order from "@/views/common/Order"
import IconAli from "@/views/common/IconAli";

export default {
  //组件名称
  name: "Menu",
  //组件注册
  components: {IconAli, Order},
  data() {
    return {
      menu: {
        id: "",
        label: "",
        menuId: "",
        menuName: "",
        menuCode: "",
        menuIcon: "",
        menuPid: "",
        menuList: "",
        showOrder: "",
        children: [],
        type: "",
      },
      data: [{id: "", label: "", children: []}],
      defaultProps: {
        children: 'children',
        label: 'label',
      },
      //展开节点的数组
      openKeyArr: [],
      //勾选节点的数组
      checkKeyArr: [],
      //当前选中节点的Key
      currentNodeKey: "",
      //搜索的单词,过滤
      select: "",
      //当前被选中的节点
      currentCheckedNodes: [],
      //当前节点
      currentCheckNode: {},
      //更新提示框
      updateFlag: false,
      //删除提示框
      deleteFlag: false,
      //排序
      orderFlag: false,
      orderMenuList: [],
      orderMenuIdList: [],
    };
  },
  //计算属性
  computed: {},
  //数据监听
  watch: {
    //监听输入框
    select(value) {
      this.$refs.tree.filter(value);
    }
  },
  //方法
  methods: {
    //排序数据
    orderCommon(menuList){
      this.orderFlag = true
      this.orderMenuList.length = 0;

      for (let i = 0; i < menuList.length; i++) {
        let obj = menuList[i];
        let tmp = {
          id: obj.menuId,
          title: obj.menuName
        }
        this.orderMenuList.push(tmp);
      }
    },
    //一级排序
    orderBase(){
      this.orderCommon(this.data)
    },
    //二级排序
    order(event, data) {
      let children = data.children;
      this.orderCommon(children)
    },
    getOrderMenuList(data) {
      this.orderMenuIdList = data.idList
    },
    //排序确定
    orderMenuOk() {
      let data = this.orderMenuIdList
      this.axios.put(`/login/menu/menu/order`,data)
          .then((data) => {
            this.selectMenuList();
            this.$Message.success('菜单排序成功');
          })
    },
    //添加子菜单
    add(event, data) {
      // event.stopPropagation();
      //todo 事件捕获
      this.currentCheckNode = null;
      let internal = setInterval(() => {
        if (this.currentCheckNode != null || this.currentCheckNode.menuPid == 0) {//说明点击的父节点
          clearInterval(internal)
          this.insertMenu(this.currentCheckNode.menuId)
        }
      }, 50)
    },
    //添加菜单
    insertMenu(pid) {
      let data = {
        menuId: "",
        menuName: "",
        menuCode: "",
        menuIcon: "",
        menuPid: pid,
        showOrder: "",
      }
      this.axios.post(`/login/menu/menu/${pid}`, data)
          .then((data) => {
            this.selectMenuList();
            this.$Message.success('菜单添加成功,请及时修改相关配置');
          })
    },
    //更新菜单
    updateMenu() {
      let data = {
        menuId: this.currentCheckNode.menuId,
        menuName: this.currentCheckNode.menuName,
        menuCode: this.currentCheckNode.menuCode,
        menuIcon: this.currentCheckNode.menuIcon,
      }
      this.axios.put(`/login/menu/menu/${this.currentCheckNode.menuId}`, data)
          .then((data) => {
            this.selectMenuList();
            this.$Message.success('菜单更新成功!');
          })
    },
    //删除
    sub(event, data) {
      // event.stopPropagation();
      this.currentCheckNode = null;

      let internal = setInterval(() => {
        if (this.currentCheckNode != null || this.currentCheckNode.menuPid == 0) {//说明点击的父节点
          clearInterval(internal)
          // 删除菜单信息
          this.deleteFlag = true
        }
      }, 50)
    },
    //删除确定
    deleteOk() {
      this.axios.delete(`/login/menu/menu/${this.currentCheckNode.menuId}`)
          .then((data) => {
            this.selectMenuList();
            this.$Message.success('删除成功!');
          })
    },
    //编辑
    edit(event, data) {
      // event.stopPropagation();
      //todo 事件捕获
      this.currentCheckNode = null;

      let internal = setInterval(() => {
        if (this.currentCheckNode != null || this.currentCheckNode.menuPid == 0) {//说明点击的父节点
          clearInterval(internal)
          // 更新菜单信息
          this.updateFlag = true
        }
      }, 50)
    },
    //更新确定
    updateOk() {
      this.updateMenu()
    },
    //更新取消
    updateCancel() {
      this.selectMenuList();
    },
    renderContent(h, {node, data, store}) {
      return (
          <div class="node" style="width:100%;display:flex;padding-top:8px;margin-top:1px">
            <div class="text" style="flex-grow: 1;">
              {node.label}
            </div>

            <div className="tvc" style="width:120px;border-radius:4px;margin-right:5px">
              {data.menuCode}
            </div>

            <div className="tvc" style="width:160px;border-radius:4px;margin-right:5px">
              {data.menuIcon}
            </div>

            <div className="tvc" style="width:20px;border-radius:4px;margin-right:6px"
                 on-click={() => this.order(event, data)}>
              <IconAli sty={{width: '25px', height: '25px', margin: '0px 2px 0px 0px'}} icon='order-1'
                       v-show={data.type == 'folder'}></IconAli>
            </div>

            <div className="tvc" style="width:20px;border-radius:4px;margin-right:5px"
                 on-click={() => this.add(event, data)}>
              <IconAli sty={{width: '20px', height: '20px', margin: '2px 2px 0px 0px'}} icon='add'
                       v-show={data.type == 'folder'}></IconAli>
            </div>

            <div className="tvc" style="width:20px;border-radius:4px;margin-right:5px"
                 on-click={() => this.edit(event, data)}>
              <IconAli sty={{width: '20px', height: '20px', margin: '2px 2px 0px 0px'}} icon='edit'></IconAli>
            </div>

            <div className="tvc" style="width:20px;border-radius:4px;margin-right:5px"
                 on-click={() => this.sub(event, data)}>
              <IconAli sty={{width: '20px', height: '20px', margin: '2px 2px 0px 0px'}} icon='sub'></IconAli>
            </div>

          </div>)
    },
    /**
     * 过滤节点
     * 返回 true 表示该节点可以展示,false隐藏
     */
    filterNode(value, data) {
      if (!value) {  //如果没有匹配到,直接返回
        return true
      }
      return data.label.indexOf(value) !== -1;
    },
    //更新节点数据
    updateKeyChildren() {
      //todo this.$refs.tree.updateKeyChildren("1",["101"])
    },
    //获取当前勾选的节点,
    getCheckedNodes() {
      this.currentCheckedNodes = this.$refs.tree.getCheckedNodes();
      // console.log(this.currentCheckedNodes);
    },
    //勾选节点
    setCheckedKeys() {
      this.$refs.tree.setCheckedKeys(["1", "201"]);
    },
    //节点被点击时调用，该节点对象,节点对应Node，节点组件
    nodeClick(data, node, nodeComponent) {
      // console.log("当前节点点击,方法被调用"); //todo debug
      this.currentCheckNode = data
    },
    //节点被鼠标右键点击时
    nodeContextMenu(event, data, node, nodeComponent) {

    },
    //节点状态发生变化时，当前对象，是否选中，子树是否有节点被选中
    checkChange(data, isChecked, isSonNodeChecked) {

    },
    //复选框被点击时触发，当前对象，当前状态对象(该对象4个属性) checkedNodes、checkedKeys、halfCheckedNodes、halfCheckedKeys
    check(data, statusData) {

    },
    //当前选中节点变化时触发,当前节点的数据，当前节点的 Node 对象
    currentChange(data, node) {

    },
    //节点被展开时触发,传递给 data 属性的数组中该节点所对应的对象、节点对应的 Node、节点组件本身
    nodeExpand(data, node, nodeComponent) {

    },
    //节点被关闭时触发,传递给 data 属性的数组中该节点所对应的对象、节点对应的 Node、节点组件本身
    nodeCollapse(data, node, nodeComponent) {
    },
    //节点开始拖拽时触发的事件
    handleDragStart(node, ev) {
      console.log('drag start', node);
    },
    //拖拽进入其他节点时触发
    handleDragEnter(draggingNode, dropNode, ev) {
      console.log('tree drag enter: ', dropNode.label);
    },
    //拖拽离开某个节点时触发的
    handleDragLeave(draggingNode, dropNode, ev) {
      console.log('tree drag leave: ', dropNode.label);
    },
    //在拖拽节点时触发的事件（类似浏览器的 mouseover 事件）
    handleDragOver(draggingNode, dropNode, ev) {
      console.log('tree drag over: ', dropNode.label);
    },
    //拖拽结束时（可能未成功）触发的事件
    handleDragEnd(draggingNode, dropNode, dropType, ev) {
      console.log('tree drag end: ', dropNode && dropNode.label, dropType);
    },
    //拖拽成功完成时触发
    handleDrop(draggingNode, dropNode, dropType, ev) {
      console.log('tree drop: ', dropNode.label, dropType);
    },
    //判断能够目标节点能否被放置
    allowDrop(draggingNode, dropNode, type) {
      if (dropNode.data.label === '二级 3-1') {
        return type !== 'inner';
      } else {
        return true;
      }
    },
    //判断节点是否能被拖拽
    allowDrag(draggingNode) {
      return draggingNode.data.label.indexOf('三级 3-2-2') === -1;
    },
    //数节点格式化
    converter(tmp) {
      for (let i of tmp) {
        i.id = i.menuId;
        i.label = i.menuName;
        i.children = i.menuList;
        i.type = 'file'
        if (i.menuCode == "user" || i.menuCode == "role" || i.menuCode == "file" || i.menuCode == "menu") {
          i.disabled = true;
        }
      }
      return tmp;
    },
    //请求菜单列表
    selectMenuList() {
      this.axios.get(`/login/menu/menus`)
          .then((data) => {
            let tmp = data.data;
            tmp = this.converter(tmp);
            for (let i of tmp) {
              i.type = 'folder'
              this.converter(i.children)
              if (i.menuCode == "sys") {
                i.disabled = true;
              }
            }
            this.data = tmp;
          })
    },
  },
//钩子函数
  mounted() {
    this.selectMenuList();
  }
}
</script>

<style scoped lang="less">

.menu-container {
  display: flex;
  flex-wrap: wrap;
}

.box {
  width: 98%;
  height: 100px;
  margin-top: 10px;
  margin-left: 1%;
}

.tree {
  width: 98%;
  align-self: stretch;
  margin-left: 1%;
  padding-top: 20px;
  display: flex;
  flex-direction: column;

  .top-box {
    width: 500px;
    padding-top: 2px;
    display: flex;
    flex-direction: row;
    justify-content: flex-end;

    .order {

    }

    .add {
    }
  }
}

/deep/ .el-tree-node__content {
  height: 40px;
}

.delTip {
  font-size: 20px;
  font-weight: bold;
  color: #F00;
}

.nodeTip {
  margin-top: 6px;
  font-size: 16px;
  color: #000;
  font-weight: bolder;
}
</style>










































